import type { IExecuteParams } from "~/execute.js";
import { execute } from "~/execute.js";
import { NotEnoughRemainingTimeError } from "~/NotEnoughRemainingTimeError.js";
import pRetry from "p-retry";
import { getNumberEnvVariable } from "./helpers/getNumberEnvVariable.js";

const minRemainingSecondsToTimeout = 120;

const MAX_PROCESSOR_PERCENT = getNumberEnvVariable(
    "MAX_ES_PROCESSOR",
    process.env.NODE_ENV === "test" ? 101 : 98
);

export interface IExecuteWithRetryParams extends Omit<IExecuteParams, "maxProcessorPercent"> {
    maxRetryTime?: number;
    retries?: number;
    minTimeout?: number;
    maxTimeout?: number;
    maxProcessorPercent?: number;
}

export const executeWithRetry = async (params: IExecuteWithRetryParams) => {
    const maxRetryTime = getNumberEnvVariable(
        "WEBINY_DYNAMODB_TO_ELASTICSEARCH_MAX_RETRY_TIME",
        params.maxRetryTime || 300000
    );
    const retries = getNumberEnvVariable(
        "WEBINY_DYNAMODB_TO_ELASTICSEARCH_RETRIES",
        params.retries || 20
    );
    const minTimeout = getNumberEnvVariable(
        "WEBINY_DYNAMODB_TO_ELASTICSEARCH_MIN_TIMEOUT",
        params.minTimeout || 1500
    );
    const maxTimeout = getNumberEnvVariable(
        "WEBINY_DYNAMODB_TO_ELASTICSEARCH_MAX_TIMEOUT",
        params.maxTimeout || 30000
    );

    try {
        await pRetry(
            execute({
                timer: params.timer,
                maxRunningTime: params.maxRunningTime,
                maxProcessorPercent: params.maxProcessorPercent || MAX_PROCESSOR_PERCENT,
                context: params.context,
                operations: params.operations
            }),
            {
                maxRetryTime,
                retries,
                minTimeout,
                maxTimeout,
                onFailedAttempt: ({ error, attemptNumber }) => {
                    if (params.timer.getRemainingSeconds() < minRemainingSecondsToTimeout) {
                        throw new NotEnoughRemainingTimeError(error);
                    }
                    /**
                     * We will only log attempts which are after 3/4 of total attempts.
                     */
                    if (attemptNumber < retries * 0.75) {
                        return;
                    }
                    console.error(`Attempt #${attemptNumber} failed.`);
                    console.error(error);
                }
            }
        );
    } catch (ex) {
        // TODO implement storing of failed operations
        throw ex;
    }
};
